package com.planw.beetl.sql.marker;

import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.templateLanguages.OuterLanguageElementImpl;
import com.planw.beetl.utils.ContextIcons;
import com.planw.beetl.utils.JavaUtils;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.event.MouseEvent;
import java.util.*;

public class BeetlSqlMarkdownLineMarkerProvider implements LineMarkerProvider {

    private OuterLanguageElementImpl m_outerLanguageElement;

    @Nullable
    @Override
    public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
        return null;
    }

    @Override
    public void collectSlowLineMarkers(@NotNull List<? extends PsiElement> elements, @NotNull Collection<? super LineMarkerInfo<?>> result) {
        if (elements == null || elements.isEmpty()) {
            return;
        }
        PsiElement elemenFirst = elements.get(0);
        
        String name = elemenFirst.getContainingFile().getName();
        name = StringUtils.capitalize(StringUtils.split(name, ".")[0]);
        List<PsiClass> classMapperList = findMapperClass(name, elemenFirst.getProject());
        String text = elemenFirst.getContainingFile().getText();
        if (!checkIsBeetSql(classMapperList, text)) {
            return;
        }

        Map<String, List<PsiMethod>> psiMethodIdMap = loadMapperPsiMethod(classMapperList);

        String[] sqlRangeArray = StringUtils.split(text, "\n");
        Set<String> sqlIdSet = new HashSet<>();
        for (int i = 0; i < sqlRangeArray.length; i++) {
            String sqlId = sqlRangeArray[i];
            if (i + 1 < sqlRangeArray.length) {
                String nextSplitIs = sqlRangeArray[i + 1];
                if (StringUtils.equals(nextSplitIs.trim(), "===")) {
                    sqlIdSet.add(sqlId);
                }
            }
        }

        Set<String> addedSqlIds = new HashSet<>();
        for (PsiElement element : elements) {
            String sqlId = element.getText().trim();
            if(addedSqlIds.contains(sqlId)){
                continue;
            }

            if (!sqlIdSet.contains(sqlId)) {
                continue;
            }
            String methodName = sqlId;
            if(!psiMethodIdMap.containsKey(methodName) && psiMethodIdMap.containsKey(StringUtils.removeEnd(methodName,"$count"))){
                methodName = StringUtils.removeEnd(methodName,"$count");
            }
            List<PsiMethod> psiMethods = psiMethodIdMap.get(methodName);
            if (psiMethods == null || psiMethods.isEmpty()) {
                continue;
            }
            
            addedSqlIds.add(sqlId);
            LineMarkerInfo<PsiElement> markerInfo = new LineMarkerInfo<PsiElement>(element, element.getTextRange(),
                    ContextIcons.STATEMENT_LINE_MARKER_ICON, 4,
                    null,
                    getNavigationHandler(psiMethods),
                    GutterIconRenderer.Alignment.CENTER);
            result.add(markerInfo);
        }

    }

    protected GutterIconNavigationHandler<PsiElement> getNavigationHandler(List<PsiMethod> psiMethods) {
        return new GutterIconNavigationHandler<PsiElement>() {
            @Override
            public void navigate(MouseEvent e, PsiElement elt) {
                for (PsiMethod psiMethod : psiMethods) {
                    psiMethod.navigate(true);
                }
            }
        };
    }

    public static Map<String, List<PsiMethod>> loadMapperPsiMethod(List<PsiClass> classMapperList) {
        Map<String, List<PsiMethod>> nameMethodsMap = new HashMap<>();
        for (PsiClass psiClass : classMapperList) {
            PsiMethod[] ms = psiClass.getMethods();
            for (PsiMethod m : ms) {
                List<PsiMethod> methods = nameMethodsMap.get(m.getName());
                if (methods == null) {
                    methods = new LinkedList<>();
                    nameMethodsMap.put(m.getName(), methods);
                }
                methods.add(m);
            }
        }
        return nameMethodsMap;
    }


    protected boolean checkIsBeetSql(List<PsiClass> classMapperList, String text) {
        if (classMapperList.isEmpty()) {
            return false;
        }
        return StringUtils.contains(text, "\n===\n");
    }

    public static List<PsiClass> findMapperClass(String name, Project project) {
        List<PsiClass> classList = new LinkedList<>();
        loadMapper(classList, name + "Dao", project);
        loadMapper(classList, name + "Mapper", project);
        return classList;
    }

    protected static void loadMapper(List<PsiClass> classList, String name, Project project) {
        PsiClass[] psiClasses = JavaUtils.findClazzs(project, name);
        for (PsiClass psiClass : psiClasses) {
            if (psiClass.getSuperClass() == null) {
                continue;
            }
            PsiClass[] interFaces = psiClass.getInterfaces();
            for (PsiClass interFace : interFaces) {
                if (interFace.getQualifiedName().equalsIgnoreCase("org.beetl.sql.core.mapper.BaseMapper")) {
                    classList.add(psiClass);
                }
            }
        }
    }
}
